There is a desire to monitor or intercept running programs and change program behavior in order to identify potential future failures, diagnose program problems or increase program performance (hereafter, “changed behavior”). For example, in the field of testing, an attempt is made to exercise software in as many ways as possible, in order to catch as many programming errors as possible before releasing the software for general use. Further, it may be advantageous to inject a failure into a program to change its behavior. It may also be helpful to change behaviors without having to rebuild the program before running it again.
It may be advantageous to change or monitor the intended behavior of an executing computer program (i.e., changed behaviors). For example, a computer program is tested using a number of conventional methods, including artificially simulating a fault condition by stepping through the executable in a debugger and manually changing the instruction pointer or memory value, modifying the source code by introducing debug statements or functions into the program and observing the results during program execution, or limiting system resources and observing the program behavior under low system resource conditions.
A technology called an injector tool, inspects a program and looks at its entry and exit points (e.g., basic blocks or functions; hereafter “functions”). The injector tool employs one of the computer program testing techniques by redirecting a function to a user-supplied function. See (1) Niewiadomski, J., et al., Function Injector, U.S. patent application Ser. No. 09/503215, filed Feb. 12, 2000; (2) Hunt et al., Heavyweight and Lightweight Instrumentation, U.S. Pat. No. 6,263,491, filed Nov. 20, 1998; (3) Edwards et al., Application Program Interface for Dynamic Instrumentation of a Heterogeneous Program in a Distributed Environment, U.S. application Ser. No. 10/001280, filed Nov. 1 2001; (4) Srivastava, A. et al., Application Program Interface for Transforming Heterogeneous Programs, U.S. application Ser. No. 09/343,276, filed Jun. 30, 1999; (5) Chaiken, R. et al., Instrumentation and Optimization Tools for Heterogeneous Programs, U.S. Pat. No. 6,481,008, issued Nov. 12, 2002; (6) Keith Vogel, Method and System for Selecting Instrumentation Points in a Computer Program, U.S. Pat. No. 5,790,858, issued Aug. 4, 1998, all of which are incorporated herein by reference.
A test designer decides which function's behavior to change. An injector instruments each selected function, with an associated user-selected function with the same signature as the original function. When the instrumented functions are later executed, the changed behavior is executed instead of (or in addition to) the original functions behavior. For each selected function to be instrumented, a user selected function is executed thereby implementing the changed behavior instead of the original behavior. For example, one testing technique is redirecting a function to a user-supplied function, called a wrapper function, and the user-supplied function is able to invoke the redirected function (i.e., the original function). The problem with this prior arrangement was that each entry and exit point of the original function typically has a unique signature. So, in order to change behavior for a function, a new function had to be created with the same signature. In that case, when 1000 functions with unique signatures are selected for changed behavior (e.g., a test behavior), 1000 changed functions must be created matching each unique signature.
A signature is the combination of the function name, the parameters to the function, and the return value. The redirect function needs to look the same in terms of the signature as the original function, so it is plug-compatible. After this plug-compatible signature is injected into the original code, calls to the original function are directed to the plug-compatible function. In order to instrument 200 functions, 200 plug compatible functions would be created.
For example, if a function being redirected has a certain signature, with three input parameters, and a return value. The input parameters have certain data types, for example, lengths and type. The redirected function needs to have the same type signature. FIG. 1, shows two example functions 100 called “swap” 102 and “order” 104. Swap 102 has two input parameters, “a” and “b” which are “int” (integer) types, and a return value, which is also an “int” type. Order 104 has two input parameters, “name1” and “name2” which are pointers to strings, and an output value which is a boolean value. In order for a function to be redirected to another function, it must contain the same signature. As shown in FIG. 2, “swap”′ (pronounced “swap prime”) 202 and “order”′ 204 have the same signature as “swap” 102 and “order” 104. Thus, they have the same number, type, and order of input parameters, and the same return values.
As shown in FIG. 2, executable instructions 206, 210, can be added to the redirected function to perform changed behavior. For example, if the function is being redirected for testing, the changed behavior may include test behavior. A test behavior could for example, run the original code 108 in most cases, but fail the function 202 by performing the changed behavior 206 of throwing an exception, every tenth time the redirected function is executed. In another testing example, the changed behavior 206, 210 may merely observe the state of an executing function (e.g., log a function's state information to disk, or view a function's state through a debugger). In another example, an original function may read or write to disk, and a redirect function may pretend that a disk read or write failed, in order to test exception handling. In another example, all input values and output values are saved for analysis. Many types of changed behaviors (e.g., tests) are known to those skilled in the art.
The redirected swap function 202 may include a portion of the original swap body 108, or may just include the change behavior 206. By using a code injector, any call to “swap” 102 in the programs binary code could be replaced with a call to “swap”′ 202 without re-compiling the program. An injector tool will perform this injection after the build of the program containing “swap” 102 and “order” 104. An injection tool is used to instrument the binary with changed behavior instead of modifying and recompiling the source code. The code could also be injected at run-time while the original binary code is executing. In this example, for each redirected function, a test developer was required to write a matching signature 202, 204 and write code 206, 210 within each redirected signature 202, 204, to implement the change behavior. This newly written code needs to be compiled, so when pointers to it are injected in the original binary code, it is ready to execute.
As shown in FIG. 3, there are also tools that generate the signature for one or more identified redirect functions. The tool would provide the signatures 302, 304, and a test designer would then write the code 306 to create the desired change behavior within the function. In this example, for a redirected function 102, 104, the tool creates the matching signature 302, 304 (and possibly a copy of the original body), but the test developer is required to write code 210 within each signature 306, to implement the change behavior. This newly written code needs to be compiled, so when pointers to it are injected in the original binary code, it is ready to execute.
The present invention is directed towards providing a function, that executes regardless of the signature of a function whose behavior it changes. Such a generic function changes behavior for plural functions. In one implementation, a generic wrapper function, could be used to change the behavior of many original functions. Of course, a generic wrapper function could be used in conjunction with the conventional wrappers described above. However, for groups of functions using a generic wrapper function, time is saved since a unique signature function need not be created for each. For example, if a generic wrapper function is used to change the behavior of 100 selected functions (associated functions), then 100 separate signatures don't have to be created. This saves time since one or a few generic wrapper functions can be created that contain the desired changed behaviors.
In another respect, a generic wrapper function can be built that performs a set of redefined functions. Such a pre-built wrapper function could be used to test a set of predefined criteria. This pre-built wrapper function is beneficial since a specific test developer, may not have the knowledge to create an equivalent function in which to do the redirection. Thus the generic wrapper function expands the type of problems test developers can address, and expands who will be able to use the technology. For example, in logging, a set of canned solutions can be provided to a test developer, and selected for test. In one embodiment, these canned solutions have already compiled binaries available for certain common behaviors. In this respect, the test developer doesn't need to write the code to implement the desired changed behavior, nor compile the source code. For example, a canned generic wrapper function behavior logs a trace of a sequence of executing functions in a program. The log includes parameter values from the executing functions which is useful for diagnosing problems in a live environment, while the program is running. This is valuable because many problems only surface in a live environment. Such a canned generic wrapper behavior outputs these values to the log file. Another canned generic behavior arbitrarily injects failures in the return value of selected functions. Such a canned generic wrapper behavior is useful to bring an instrumented program into a state where failures are more likely to be exposed. A test developer selects multiple functions to be wrapped with a canned generic wrapper function behavior.
In a further respect, a descriptor describes a context of the original function (e.g., function name, function address, parameter names, parameter types, parameter values, etc.). If a generic wrapper function performs a changed behavior that is ,dependent on the context of the original function, then the descriptor can be used to determine which of the plural changed behaviors to perform.
In yet another implementation, a generic wrapper function includes behavior executed before execution of the body of the original function (preprocessing), and includes behavior executed after execution of the body of the original function (postprocessing). In one such implementation, a call to a preprocessing generic wrapper function is inserted (e.g., injected) in the original function before the function body, and a call to a postprocessing generic wrapper is inserted in the original function just before each return instruction in the original function. In another such implementation, only a call to the preprocessing generic wrapper function is inserted in the original function, and instructions in the preprocessor replace a return address in the stack frame with the postprocessing address, so upon return from executing the body, control flow is automatically sent to postprocessing. In yet another implementation, only one call to the generic wrapper function is inserted in the original function body. However, the call includes an address of the original function, that can be used by the generic wrapper function to execute the original function body or obtain other information about its calling context.
In yet another respect, a method instruments a function in an executable file so that the instrumented function calls a generic preprocessor prior to execution of the body of the function. After the preprocessor modifies the original function's incoming parameters, the body of the function itself is executed. Finally, execution is directed to a generic postprocessor prior to returning from the function. The postprocessor modifies the outgoing parameters and return value. In one such implementation, during instrumentation of target functions in the executable file, the parameters of an instrumented function are described and packaged into a descriptor data structure. The descriptor data structure is passed to the generic preprocessor and postprocessor. At runtime, a function parameter and other function values or references can be obtained through the descriptor data structure.
Additional features and advantages will be made apparent from the following detailed description of the illustrated embodiment which proceeds with reference to the accompanying drawings.